package com.myk.game.gobangchess.rooms.oldrooms;

import com.myk.game.gobangchess.Room;
import com.myk.game.gobangchess.constants.BoardStyleData;
import com.myk.game.gobangchess.constants.WinMode;
import com.myk.game.gobangchess.signs.AroundSign;
import com.myk.game.gobangchess.signs.LineSign;
import com.myk.game.gobangchess.signs.TextSign;
import com.myk.game.gobangchess.utils.GridsUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 连角黑白棋
 */
public class ConnectCornersReversiRoom extends Room {

    private final List<Integer> signIds = new ArrayList<>(); //当前翻子集合，第一个为触发点
    private int[] lastMove = null;//对方上一次落子的坐标

    public ConnectCornersReversiRoom(String roomName) {
        super(roomName, "\t\t新棋吧老友，sng玩家设计的黑白棋变体。", "连角黑白棋", BoardStyleData.CONNECT_CORNERS_12X12_STYLE, true, true);
    }

    @Override
    public String getGameInfo() {
        if (lastMove != null) {
            //以lastMove为坐标绘制用LineSing绘制红色交叉线
            int pRow = lastMove[0];
            int pCol = lastMove[1];
            int maxRow = curGrids.length - 1;
            int maxCol = curGrids[0].length - 1;
            if (step > 1){
                signList.add(new LineSign(getIndex(pRow, 0), getIndex(pRow, maxCol), "#99FF0000", 0));
                signList.add(new LineSign(getIndex(0, pCol), getIndex(maxRow, pCol), "#99FF0000", 0));
                int startDiff = Math.min(pRow, pCol);
                int endDiff = Math.min(maxRow - pRow, maxCol - pCol);
                signList.add(new LineSign(getIndex(pRow - startDiff, pCol - startDiff), getIndex(pRow + endDiff, pCol + endDiff), "#99FF0000", 0));
                int startDiff2 = Math.min(maxRow - pRow, pCol);
                int endDiff2 = Math.min(pRow, maxCol - pCol);
                signList.add(new LineSign(getIndex(pRow + startDiff2, pCol - startDiff2), getIndex(pRow - endDiff2, pCol + endDiff2), "#99FF0000", 0));
            }
        }
        else {
            signList.removeIf(sign -> sign instanceof LineSign);
        }
        return super.getGameInfo();
    }

    @Override
    public String getRuleText() {
        return "《连角黑白棋》\n" +
                "一般情况中间十路可以随意落子，边角处必须能翻子（黑白棋的翻转）才能落子。\n" +
                "落子不能落在对手上一个棋子的直线斜线上，但白方的第一次落子无此限制。\n" +
                "落子后判断是否夹住对方棋子的规则和普通黑白棋一样，落子后对方不能落子我方继续落子。\n" +
                "夹棋后直线斜线8个方向分别判断，所夹棋子数量小于等于5时这些棋子会被翻转。\n" +
                "落子和翻转棋子后，己方颜色的棋子在棋盘对角形成通路，接下来一回合对方不能改变连通则己方获胜。\n" +
                "注意，颜色相同直线相连的才能算相通。\n" +
                "双方都不能落子时没有形成通路则和棋。";
    }

    @Override
    public void resetGrids() {
        super.resetGrids();
        signIds.clear();
        lastMove = null;
        //updateScores(); //每次刷新重置棋盘都要重新算分数
    }

    @Override
    public void doRule(int row, int col, int value) {
        if (value == 1) {
            if (ifMovePiecesSuccess(row, col, value)) { //黑棋落子完成后

                //我方落子后如果没有打破对方的联通则对方获胜
                if (isConnectCorner(2)){
                    onGameOver(WinMode.WHITE_WIN);
                    return;
                }

                //updateScores(); //成功落子就修改分数
                //先判断白棋是否无路可走
                lastMove = new int[]{row, col}; //对方判断时要考虑到共线限制
                if (checkGameOver(2)) { //判断对方是否无路可走

                    //我方联通后，下回合对方不能行动我方直接获胜啊
                    if (isConnectCorner(1)){
                        onGameOver(WinMode.BLACK_WIN);
                        return;
                    }

                    //如果白棋无路可走，再判断黑棋是否也是无路可走
                    lastMove = null; //自己判断没有共线限制
                    if (checkGameOver(1)) {
                        //白棋黑棋都无路可走，那么游戏结束，去判断胜负吧，没有形成通路直接平局
                        onGameOver(WinMode.BOTH_DRAW);
                    }
                    else {
                        //黑棋继续走
                        stepAdd();
                        noticeBlackMove();
                    }
                }
                else {
                    //提示白棋落子
                    lastMove = new int[]{row, col}; //记录坐标，对方下次不能与之共线
                    stepAdd();
                    noticeWhiteMove();
                }
            }
        }
        if (value == 2) {
            if (ifMovePiecesSuccess(row, col, value)) { //白棋落子完成后
                //我方落子后如果没有打破对方的联通则对方获胜
                if (isConnectCorner(1)){
                    onGameOver(WinMode.BLACK_WIN);
                    return;
                }
                //updateScores();  //成功落子就修改分数
                //先判断黑棋是否无路可走
                lastMove = new int[]{row, col}; //对方判断时要考虑到共线限制
                if (checkGameOver(1)) {

                    //我方联通后，下回合对方不能行动我方直接获胜啊
                    if (isConnectCorner(2)){
                        onGameOver(WinMode.WHITE_WIN);
                        return;
                    }

                    lastMove = null; //自己继续走没有共线限制
                    if (checkGameOver(2)) {
                        onGameOver(WinMode.BOTH_DRAW);
                    }
                    else {
                        stepAdd();
                        noticeWhiteMove();
                    }
                }
                else {
                    //提示黑棋落子
                    lastMove = new int[]{row, col}; //记录坐标，对方下次不能与之共线
                    stepAdd();
                    noticeBlackMove();
                }
            }
        }
    }

    /**
     * 判断是否形成通路
     */
    private boolean isConnectCorner(int side) {
        if (side == 1){
            return hasPath(curGrids,0,0,curGrids.length-1,curGrids[0].length-1,1);
        }
        else if (side == 2){
            return hasPath(curGrids,0,curGrids[0].length-1,curGrids.length-1,0,2);
        }
        return false;
    }

    //修改分数
    private void updateScores() {
        blackScore = GridsUtils.getDataCount(curGrids, 1);
        whiteScore = GridsUtils.getDataCount(curGrids, 2);
    }

    //执行落子操作和判断落子是否成功
    private boolean ifMovePiecesSuccess(int row, int col, int value) {
        if (curGrids[row][col] != 0) { //如果该位置不为空不能落子
            return false;
        }
        if (step != 1 && isSameLine(row, col)) { //如果和对方上一步的棋子在同一条直线斜线上，则不能落子(白方第一次落子除外，黑方第一次落子后，step就变成1了然后等待白方第一次落子)
            return false;
        }

        //处理翻子逻辑
        List<List<int[]>> usefulDirList = getUsefulEightDirGridsLineList(row, col, value); //有效的八个方向的棋子链
        boolean isHasOverturn = false;
        for (int i = 0; i < usefulDirList.size(); i++) {
            //遍历所有有效的链表，执行翻转棋子，如果能翻转记个数
            if (ifOverturnSuccess(row, col, value, usefulDirList.get(i))) {
                isHasOverturn = true;
            }
        }
        if (isOnEdge(row, col)) { //如果落在边角处
            if (!isHasOverturn) { //不能翻子就不能落子
                return false;
            }
        }

        //落子成功更新标记画面
        curGrids[row][col] = value;//落子
        signList.clear();
        signList.add(new TextSign(getIndex(row, col), "※", "#FFDDAA33"));
        signIds.add(getIndex(row, col)); //把盘子触发点也加进来
        signList.add(new AroundSign(new ArrayList<>(signIds), "#6600FF00", "#9900ff00", 1.2f));
        signIds.clear();
        return true;
    }

    //判断指定落点是否在棋盘的边角
    private boolean isOnEdge(int row, int col) {
        return row == 0 || col == 0 || row == curGrids.length - 1 || col == curGrids[0].length - 1;
    }

    //判断指定落点和对方上一步的坐标是否直线斜线共线
    private boolean isSameLine(int row, int col) {
        if (lastMove != null) {
            return lastMove[0] == row || lastMove[1] == col || Math.abs(row - lastMove[0]) == Math.abs(col - lastMove[1]);
        }
        return false;
    }


    //棋子翻转功能
    private boolean ifOverturnSuccess(int row, int col, int value, List<int[]> list) {
        List<int[]> tempCanOverList = new ArrayList<>(); //临时保存预计可翻的棋子列表
        boolean canOver = false; //这组列表是否可翻
        for (int[] i : list) {
            if (i[2] != value) {
                if (i[2] != 0 && i[2] != 9) { //别忘了空白和墙的情况要排除
                    tempCanOverList.add(i); //颜色不相同的加进临时可翻棋子列表
                }
                else {
                    break;
                }
            }
            else {
                if (tempCanOverList.size() > 5){ //最多翻5个
                    break;
                }
                else {
                    canOver = true; //一旦遇到相同的列表，那么就中断遍历开始翻转，因为之前数据验证过，所以这里不用对第一个格子进行校验
                    break;
                }
            }
        }
        if (canOver) {
            curGrids[row][col] = value; //落下棋子
            for (int[] can : tempCanOverList) {
                curGrids[can[0]][can[1]] = value; //翻转棋子
            }
            addOverSigns(tempCanOverList); //更新标记
        }
        return canOver;
    }

    //添加反转棋子标记
    private void addOverSigns(List<int[]> tempCanOverList) {
        List<Integer> ids = new ArrayList<>();
        for (int[] can : tempCanOverList) {
            ids.add(getIndex(can[0], can[1]));
        }
        signIds.addAll(ids);
    }

    //判断是否无路可走，value=1判断是否黑棋无路可走，value=2判断是否白棋无路可走
    private boolean checkGameOver(int value) {
        int row = curGrids.length;
        int col = curGrids[0].length;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (thisPointCanMove(i, j, value)) {
                    return false;//一旦有能下value棋的地方，就返回false
                }

            }
        }
        return true; //所有格子都不能走游戏结束
    }

    //当游戏结束时，各种操作
    private void onGameOver(int winMode) {
        if (winMode == WinMode.BLACK_WIN) {
            String blackWinMsg = player_black.getNickNameOrTemp() + "执黑棋战胜" + player_white.getNickNameOrTemp();
            doOver(WinMode.BLACK_WIN, blackWinMsg);
        }
        else if (winMode == WinMode.WHITE_WIN) {
            String whiteWinMsg = player_white.getNickNameOrTemp() + "执白棋战胜" + player_black.getNickNameOrTemp();
            doOver(WinMode.WHITE_WIN, whiteWinMsg);
        }
        else {
            String bothWinMsg = player_black.getNickNameOrTemp() + "-黑棋战平" + player_white.getNickNameOrTemp() + "-白棋";
            doOver(WinMode.BOTH_DRAW, bothWinMsg);
        }
    }

    //（不执行落子操作）仅判断落子是否成功
    private boolean thisPointCanMove(int row, int col, int value) {
        if (curGrids[row][col] != 0) { //如果该位置不为空不能落子
            return false;
        }
        if (isSameLine(row, col)) { //如果和对方上一步的棋子在同一条直线斜线上，则不能落子
            return false;
        }

        List<List<int[]>> usefulDirList = getUsefulEightDirGridsLineList(row, col, value); //有效的八个方向的棋子链
        boolean isHasOverturn = false;
        for (List<int[]> list : usefulDirList) {
            //遍历所有有效的链表，判断是否能翻转，如果能翻转记个数（只是判断，不实际做翻转的动作）
            if (thisPointCanOverTurnList(row, col, value, list)) {
                isHasOverturn = true;
            }
        }
        if (isOnEdge(row, col)) { //如果落在边角处
            if (!isHasOverturn) { //不能翻子就不能落子
                return false;
            }
        }
        return true;
    }

    //获取当前位置格子的八个方向到边的格子链，顺序为从向上方向开始逆时针直到向右上方向。
    private List<List<int[]>> getUsefulEightDirGridsLineList(int row, int col, int value) {
        int boardRows = curGrids.length; //棋盘总行数
        int boardCols = curGrids[0].length; //棋盘总列数
        List<List<int[]>> eightDirList = new ArrayList<>(); //八方向的链表
        //上
        List<int[]> topList = new ArrayList<>(); //往上走的链表
        int topLen = row; //距离边的格数
        for (int i = 1; i <= topLen; i++) {
            int nRow = row - i;
            int nCol = col;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            topList.add(g);
        }
        eightDirList.add(topList);
        //左上
        List<int[]> leftTopList = new ArrayList<>();
        int leftTopLen = Math.min(row, col);
        for (int i = 1; i <= leftTopLen; i++) {
            int nRow = row - i;
            int nCol = col - i;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            leftTopList.add(g);
        }
        eightDirList.add(leftTopList);
        //左
        List<int[]> leftList = new ArrayList<>();
        int leftLen = col;
        for (int i = 1; i <= leftLen; i++) {
            int nRow = row;
            int nCol = col - i;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            leftList.add(g);
        }
        eightDirList.add(leftList);
        //左下
        List<int[]> leftBottomList = new ArrayList<>();
        int leftBottomLen = Math.min(boardRows - (row + 1), col);
        for (int i = 1; i <= leftBottomLen; i++) {
            int nRow = row + i;
            int nCol = col - i;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            leftBottomList.add(g);
        }
        eightDirList.add(leftBottomList);
        //下
        List<int[]> bottomList = new ArrayList<>(); //往上走的链表
        int bottomLen = boardRows - (row + 1);
        for (int i = 1; i <= bottomLen; i++) {
            int nRow = row + i;
            int nCol = col;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            bottomList.add(g);
        }
        eightDirList.add(bottomList);
        //右下
        List<int[]> rightBottomList = new ArrayList<>();
        int rightBottomLen = Math.min(boardRows - (row + 1), boardCols - (col + 1));
        for (int i = 1; i <= rightBottomLen; i++) {
            int nRow = row + i;
            int nCol = col + i;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            rightBottomList.add(g);
        }
        eightDirList.add(rightBottomList);
        //右
        List<int[]> rightList = new ArrayList<>();
        int rightLen = boardCols - (col + 1);
        for (int i = 1; i <= rightLen; i++) {
            int nRow = row;
            int nCol = col + i;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            rightList.add(g);
        }
        eightDirList.add(rightList);
        //右上
        List<int[]> rightTopList = new ArrayList<>();
        int rightTopLen = Math.min(row, boardCols - (col + 1));
        for (int i = 1; i <= rightTopLen; i++) {
            int nRow = row - i;
            int nCol = col + i;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            rightTopList.add(g);
        }
        eightDirList.add(rightTopList);

        //把不符合要求的淘汰掉
        List<List<int[]>> useLessList = new ArrayList<>();
        for (List<int[]> list : eightDirList) {
            if (list.size() <= 1) {//链表至少有两个格子才可能形成夹棋
                useLessList.add(list);
            }
            else if (list.get(0)[2] == value) { //相邻的格子得为异色
                useLessList.add(list);
            }
        }
        for (List<int[]> list : useLessList) {
            eightDirList.remove(list);
        }

        return eightDirList;
    }

    //判断若下在该位置，该列是否有棋子能翻转，仅在 thisPointCanMove() 中用
    private boolean thisPointCanOverTurnList(int row, int col, int value, List<int[]> list) {
        List<int[]> tempCanOverList = new ArrayList<>(); //临时保存预计可翻的棋子列表
        boolean canOver = false; //这组列表是否可翻
        for (int[] i : list) {
            if (i[2] != value) {
                if (i[2] != 0 && i[2] != 9) { //别忘了空白和墙的情况要排除
                    tempCanOverList.add(i); //颜色不相同的加进临时可翻棋子列表
                }
                else {
                    break;
                }
            }
            else {
                canOver = true; //一旦遇到相同的列表，那么就中断遍历开始翻转，因为之前数据验证过，所以这里不用对第一个格子进行校验
                break;
            }
        }
        return canOver;
    }

    //路径联通搜索算法
    private static boolean hasPath(int[][] board, int startRow, int startCol, int endRow, int endCol, int player) {
        if (board[startRow][startCol] != player || board[endRow][endCol] != player) {
            return false; // 如果起始或结束位置不是玩家的颜色，则没有路径
        }
        boolean[][] visited = new boolean[board.length][board[0].length];
        return dfs(board, startRow, startCol, endRow, endCol, player, visited);
    }

    //深度优先算法
    private static boolean dfs(int[][] board, int row, int col, int endRow, int endCol, int player, boolean[][] visited) {
        if (row < 0 || col < 0 || row >= board.length || col >= board[row].length || visited[row][col] || board[row][col] != player) {
            return false;
        }
        if (row == endRow && col == endCol) {
            return true; // 找到了一条路径
        }
        visited[row][col] = true;
        // 检查上下左右四个方向
        return dfs(board, row - 1, col, endRow, endCol, player, visited) ||
                dfs(board, row + 1, col, endRow, endCol, player, visited) ||
                dfs(board, row, col - 1, endRow, endCol, player, visited) ||
                dfs(board, row, col + 1, endRow, endCol, player, visited);
    }
}
